/**
 * @file   Vector.h
 * @author WinterMelonCDY <wintermeloncdy@wintermeloncdy-VirtualBox>
 * @date   Wed Oct 13 19:16:46 2021
 * 
 * @brief  
 * 
 * 
 */
#include<algorithm>
/**
 * @brief   vector class 
 * @tparam Object       the type of objects in the container
 */
template<typename Object>
class Vector
{
    public:
        /**
         * @brief  Construct a new Vector object. Setting the value of the size and capacity, allocating the memory space.
         * @param  initSize    the initialized size of vector, the default size is zero, and the default capacity is initial size add the constant spare capacity.
         */
        explicit Vector(int initSize = 0) : theSize { initSize } , theCapacity { initSize + SPARE_CAPACITY }
        {
            objects = new Object[ theCapacity ];
        }

        /**
         * @brief Copy constructor
         * @param  rhs  the vector to be copied
         */
        Vector(const Vector & rhs):theSize{rhs.theSize},theCapacity{rhs.theCapacity},objects{nullptr}
        {
            objects = new Object[ theCapacity ];
            for(int k = 0; k < theSize;k++)
            {
                objects[k] = rhs.objects[k];
            }
        }

        /**
         * @brief Copy assignment
         * @param  rhs              
         * @return Vector& 
         */
        Vector & operator= (const Vector & rhs)
        {
            Vector copy = rhs;
            std::swap(*this,copy);
            return *this;
        }

        /**
         * @brief Destroy the Vector object, relese the objects' memory.
         */
        ~Vector()
        {
            delete[] objects;
        }

        /**
         * @brief  Move constructor
         * @param  rhs
         */
        Vector(Vector && rhs):theSize{rhs.theSize} , theCapacity{rhs.theCapacity} , objects{rhs.objects}
        {
            rhs.objects = nullptr;
            rhs.theSize = 0;
            rhs.theCapacity = 0;
        }
    
        /**
         * @brief  Move assignment
         * @param  rhs
         */
        Vector & operator= (Vector &&rhs )
        {
            std::swap(theSize,rhs.theSize);
            std::swap(theCapacity,rhs.theCapacity);
            std::swap(objects,rhs.opjects);
            return *this;
        }
        
        /**
         * @brief  Change the value of size. If the new size is larger than the capacity, call the reverse function to allocate new memory to expand capacity.
         * @param  newSize          the number of new size
         */
        void resize(int newSize)
        {
            if(newSize > theCapacity)
                reserve(newSize * 2);
            theSize = newSize;
        }

        /**
         * @brief  If the new capacity is smaller than the size, skip. Else allocate new memory space and then copy the original data.
         * @param  newCapacity      the number of new capacity
         */
        void reserve(int newCapacity)
        {
            if(newCapacity < theSize)
                return;
            Object *newArray = new Object[newCapacity];
            for(int k = 0 ; k < theSize ; k++)
                newArray[k] = std::move( objects[k] );
            
            theCapacity = newCapacity;
            std::swap(objects,newArray);
            delete[] newArray;
        }

        /**
         * @brief return the element given a position.
         * @param index  the location of the element
         */
        Object & operator[](int index)
        {
            return objects[index];
        }

        const Object &  operator[](int index) const
        {
            return objects[index];
        }

        /**
         * @brief  check whether the vector is empty.
         * @return true if the vector is empty, i.e. the size is 0.
         * @return false if the vector is not empty.
         */
        bool empty() const
        {
            return size() == 0;
        }

        /**
         * @brief  return the size of vector
         * @return int 
         */
        int size() const
        {
            return theSize;
        }

        /**
         * @brief return the capacity of vector
         * @return int 
         */
        int capacity() const
        {
            return theCapacity;
        }

        /**
         * @brief  add a new element in vector. If the capacity is not enough, call the reserve function to allocate new memory space.
         * @param  x                My Param doc
         */
        void push_back(const Object & x)
        {
            if(theSize == theCapacity)
                reserve(2*theCapacity+1);
            objects[theSize++] = x;
        }

        void push_back(Object && x)
        {
            if(theSize == theCapacity)
                reserve(2*theCapacity+1);
            objects[theSize++] = std::move(x);
        }

        /**
         * @brief Delete the last element just to minus the size.
         */
        void pop_back()
        {
            --theSize;
        }

        const Object & back () const
        {
            return objects[ theSize - 1 ];
        }

        typedef Object * iterator;              // pointor to an element whose type is Object
        typedef const Object * const_iterator;  // the constant pointor

        /**
         * @brief return the first element in the vector
         * @return iterator 
         */
        iterator begin()
        {
            return &objects[0];
        }
        const_iterator begin() const
        {
            return &objects[0];
        }

        /**
         * @brief  return the last element in the vector
         * @return iterator 
         */
        iterator end()
        {
            return &objects[size()];
        }
        const_iterator end() const
        {
            return &objects[size()];
        }
        
        // static const int SPARE_CAPACITY = 16;
        
        private:
            int theSize;        // the number of the elements in the vector
            int theCapacity;    // the number of the allocated memory, which should be larger or equal to the size
            Object * objects;   // the objects(elements) in the vector
             
};
